#ifndef IMAGE_ITERATORS_HPP
#define IMAGE_ITERATORS_HPP
#include "image.hpp"

#include <boost/shared_ptr.hpp>

namespace quadil{
namespace iiterator{

class AbstractPixelIterator{
public:
	const int left;
	const int right;
	const int top;
	const int bottom;

protected:
	AbstractPixelIterator(int l,int t,int r,int b):left(l),top(t),right(r),bottom(b){}

};





template <typename T,typename K> class AccSumIterator{
	Image<K> sumImage;
	const int maxHorizontal;
	const int maxVertical;
	const int width;
	const int height;
	K* topLeft;
	K* topRight;
	K* bottomLeft;
	K* bottomRight;
	const int lineStride;
	t_real blurSurface;
	int blurWindowW;
	int blurWindowH;
public:
	AccSumIterator(Image<T> & i,int maxWidth,int maxHeight):
		sumImage(Image<K>::createSafeBorderImage(i.width,i.height,maxWidth/2+1,maxHeight/2+1,maxWidth/2+1,maxHeight/2+1,0)),
		maxHorizontal(maxWidth/2+1),maxVertical(maxWidth/2+1),width(i.width),height(i.height),lineStride(sumImage.lineStride)
		{
		//std::cerr<<"LinearBlur()1\n";std::cerr.flush();
		i.getAccumulativeSum(sumImage,maxWidth/2+1,maxHeight/2+1);
		this->setBluringWindow(maxHorizontal,maxVertical);
	}
	void setBluringWindow(int w, int h){
		if(w>maxHorizontal||h>maxVertical){
			throw "quadil::iiterator::LinearBlur::setBluringWindow : exeded max blur window size.";
		}
		topLeft=sumImage.fp-(w/2)-(h/2)*lineStride;
		topRight=sumImage.fp+((w+1)/2-1)-(h/2)*lineStride;
		bottomLeft=sumImage.fp-(w/2)+((h+1)/2-1)*lineStride;
		bottomRight=sumImage.fp+((w+1)/2-1)+((h+1)/2-1)*lineStride;
		blurSurface=w*h;
		blurWindowW=w;
		blurWindowH=h;
	}
	K getSum(int x,int y){
		int pos=x+y*lineStride;
		return (bottomRight[pos]-topRight[pos]-bottomLeft[pos]+topLeft[pos]);
	}
	Image<t_real> getLinearBlurAsRealImage(){
		Image<t_real> res=Image<t_real>(width,height);
		t_real* outCurLine;
		int pos, y,posEnd;
		for(y=0;y<height;y++){
			pos=y*lineStride;
			posEnd=pos+width;
			outCurLine=res.getRow(y)-pos;
			while(pos!=posEnd){
				outCurLine[pos]=(bottomRight[pos]-topRight[pos]-bottomLeft[pos]+topLeft[pos])/blurSurface;
				pos++;
			}
		}
		return res;
	}
	Image<t_byte> getLinearBlurAsByteImage(){
		Image<t_byte> res=Image<t_byte>(width,height);
		t_byte* outCurLine;
		int pos, y,posEnd,posStart;
		for(y=0;y<height;y++){
			posStart=y*lineStride;
			posEnd=posStart+width;
			outCurLine=res.getRow(y);
			pos=posStart;
			while(pos!=posEnd){
				outCurLine[pos-posStart]=((bottomRight[pos]-topRight[pos]-bottomLeft[pos]+topLeft[pos])/blurSurface);
				pos++;
			}
		}
		return res;
	}
	Image<t_byte> getPiramidBlurAsByteImage(){
		Image<t_byte> res=Image<t_byte>(width,height);
		res.setAll(0);
		t_byte* outCurLine;
		int pos, y,posEnd,posStart;
		int oldWidth=blurWindowW;
		int oldHeight=blurWindowH;
		int divider=(blurWindowW<blurWindowH)?(blurWindowW-1)/2:(blurWindowH-1)/2;
		while(blurWindowW>1 && blurWindowH>1){
			for(y=0;y<height;y++){
				posStart=y*lineStride;
				posEnd=posStart+width;
				outCurLine=res.getRow(y);
				pos=posStart;
				while(pos!=posEnd){
					outCurLine[pos-posStart]+=((bottomRight[pos]-topRight[pos]-bottomLeft[pos]+topLeft[pos])/(blurSurface*10));
					pos++;
				}
			}
			this->setBluringWindow(blurWindowW-2,blurWindowH-2);
		}
		return res;
	}

};

}
}
#endif
